// Fill out your copyright notice in the Description page of Project Settings.


#include "Player/AuraPlayerController.h"

#include "AbilitySystemBlueprintLibrary.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameplayTagContainer.h"
#include "NavigationPath.h"
#include "NavigationSystem.h"
#include "AbilitySystem/AuraAbilitySystemComponent.h"
#include "Components/DamageTextComponent.h"
#include "Components/SplineComponent.h"
#include "Game/AuraGameplayTags.h"
#include "GameFramework/Character.h"
#include "Input/AuraInputComponent.h"
#include "Interaction/EnemyInterface.h"


AAuraPlayerController::AAuraPlayerController()
{
	SplineComponent = CreateDefaultSubobject<USplineComponent>(TEXT("Spline"));
}


void AAuraPlayerController::PlayerTick(const float DeltaTime)
{
	Super::PlayerTick(DeltaTime);

	// 光标悬浮在敌人身上相关逻辑
	CursorHit();
	// 自动寻路
	AutomaticPathfinding();
}


UAuraAbilitySystemComponent* AAuraPlayerController::GetASC()
{
	if (AuraAbilitySystemComponent == nullptr)
	{
		AuraAbilitySystemComponent = Cast<UAuraAbilitySystemComponent>(
			UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(GetPawn())
		);
		return AuraAbilitySystemComponent;
	}
	return AuraAbilitySystemComponent;
}

// 在PlayerController里面调用的主要原因是，为了获取到技能施放者，然后通过它自身的PC来调用函数，这个函数只在客户端运行。
void AAuraPlayerController::ShowDamageNumber_Implementation(
	const float DamageAmount, ACharacter* TargetCharacter, const bool bIsBlockedHit, const bool bIsCriticalHit
)
{
	if (IsValid(TargetCharacter) && DamageTextComponentClass)
	{
		UDamageTextComponent* TextComponent = NewObject<UDamageTextComponent>(
			TargetCharacter,
			DamageTextComponentClass
		);

		// 动态创建的组件需要调用注册
		TextComponent->RegisterComponent();
		//先附加到角色身上，使用角色位置(即相对位置)
		TextComponent->AttachToComponent(
			TargetCharacter->GetRootComponent(),
			FAttachmentTransformRules::KeepRelativeTransform
		);
		//然后从角色身上分离，使用世界变换,从一个位置播放动画
		TextComponent->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
		//设置显示的伤害数字
		TextComponent->SetDamageText(DamageAmount, bIsBlockedHit, bIsCriticalHit);
	}
}

void AAuraPlayerController::BeginPlay()
{
	Super::BeginPlay();

	check(AuraContext)

	UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<
		UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
	if (Subsystem)
	{
		Subsystem->AddMappingContext(AuraContext, 0);
	}

	bShowMouseCursor = true;
	DefaultMouseCursor = EMouseCursor::Default;

	FInputModeGameAndUI InputModeData;
	InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock); //设置视口的鼠标锁定行为
	InputModeData.SetHideCursorDuringCapture(false); //是否在鼠标按下导致的临时鼠标捕获期间隐藏光标
	SetInputMode(InputModeData);
}

void AAuraPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();

	UAuraInputComponent* EnhancedInputComponent = CastChecked<UAuraInputComponent>(InputComponent);

	EnhancedInputComponent->BindAction(
		MoveAction,
		ETriggerEvent::Triggered,
		this,
		&AAuraPlayerController::Move
	);

	EnhancedInputComponent->BindAction(
		ShiftAction,
		ETriggerEvent::Started,
		this,
		&AAuraPlayerController::OnShiftKeyStart
	);

	EnhancedInputComponent->BindAction(
		ShiftAction,
		ETriggerEvent::Completed,
		this,
		&AAuraPlayerController::OnShiftKeyReleased
	);

	EnhancedInputComponent->BindAbilityAction(
		InputConfig,
		this,
		&ThisClass::OnTaggedInputStarted,
		&ThisClass::OnTaggedInputReleased,
		&ThisClass::OnTaggedInputTriggered
	);
}

void AAuraPlayerController::OnShiftKeyStart()
{
	bShiftKeyDown = true;
}

void AAuraPlayerController::OnShiftKeyReleased()
{
	bShiftKeyDown = false;
}

void AAuraPlayerController::Move(const FInputActionValue& ValueInputAction)
{
	const FVector2D InputAxisVector = ValueInputAction.Get<FVector2D>();
	const FRotator Rotation = GetControlRotation();
	const FRotator YawRotation = FRotator(0.f, Rotation.Yaw, 0.f);

	const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
	const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

	if (APawn* ControlledPawn = GetPawn<APawn>())
	{
		ControlledPawn->AddMovementInput(ForwardDirection, InputAxisVector.Y);
		ControlledPawn->AddMovementInput(RightDirection, InputAxisVector.X);
	}
}

void AAuraPlayerController::OnTaggedInputStarted(const FGameplayTag InputTag)
{
	// 鼠标左键按下相关逻辑
	if (InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
	{
		// 鼠标在敌人身上按下时,bSelected为true
		bSelected = (ThisActor != nullptr);

		bAutomaticMovement = false;
		HoldTime = 0.f;
	}
}

void AAuraPlayerController::OnTaggedInputTriggered(const FGameplayTag InputTag)
{
	// 非鼠标左键Triggered相关逻辑
	if (!InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_LMB))
	{
		if (GetASC())
		{
			GetASC()->AbilityTaggedInputTriggered(InputTag);
		}
		return;
	}


	// 左键触发时选中敌人或者按住shift键都会触发技能
	if (bSelected || bShiftKeyDown)
	{
		if (GetASC())
		{
			GetASC()->AbilityTaggedInputTriggered(InputTag);
		}
	}
	else
	{
		HoldTime += GetWorld()->GetDeltaSeconds();
		if (CursorHitResult.bBlockingHit)
		{
			ClickPosition = CursorHitResult.ImpactPoint;

			if (HoldTime > ShortPressThreshold)
			{
				if (APawn* PossessedPawn = GetPawn())
				{
					const FVector Direction = (ClickPosition - PossessedPawn->GetActorLocation()).GetSafeNormal();
					PossessedPawn->AddMovementInput(Direction);
				}
			}
		}
	}
}

void AAuraPlayerController::OnTaggedInputReleased(const FGameplayTag InputTag)
{
	// 非鼠标左键松开相关逻辑
	if (!InputTag.MatchesTagExact(FAuraGameplayTags::AuraGameplayTags.InputTag_LMB))
	{
		if (GetASC())
		{
			GetASC()->AbilityTaggedInputReleased(InputTag);
		}
		return;
	}

	// 鼠标左键松开相关逻辑

	// 左键松开时选中敌人或者按住shift键都会触发技能
	if (bSelected || bShiftKeyDown)
	{
		if (GetASC())
		{
			GetASC()->AbilityTaggedInputReleased(InputTag);
		}
	}
	else
	{
		if (HoldTime <= ShortPressThreshold)
		{
			if (APawn* PossessedPawn = GetPawn())
			{
				if (UNavigationPath* NavPath = UNavigationSystemV1::FindPathToLocationSynchronously(
						this,
						PossessedPawn->GetActorLocation(),
						ClickPosition
					)
				)
				{
					// 清楚样条线内现有的点
					SplineComponent->ClearSplinePoints();

					for (FVector& PathPoint : NavPath->PathPoints)
					{
						SplineComponent->AddSplinePoint(PathPoint, ESplineCoordinateSpace::World);
						DrawDebugSphere(GetWorld(), PathPoint, 8.f, 8, FColor::Orange, false, 5.f);
					}
					// 将导航路径终点设为终点
					if (NavPath->IsValid())
					{
						ClickPosition = NavPath->PathPoints[NavPath->PathPoints.Num() - 1];
					}

					bAutomaticMovement = true;
				}
			}
		}
	}
}

void AAuraPlayerController::CursorHit()
{
	GetHitResultUnderCursor(ECC_Visibility, false, CursorHitResult);
	if (!CursorHitResult.bBlockingHit) { return; }

	LastActor = ThisActor;
	ThisActor = Cast<IEnemyInterface>(CursorHitResult.GetActor());

	// 射线拾取后，会出现的几种情况
	// 1. LastActor is null   ThisActor is null 不需要任何操作
	// 2. LastActor is null   ThisActor is valid 高亮ThisActor
	// 3. LastActor is valid   ThisActor is null 取消高亮LastActor
	// 4. LastActor is valid   ThisActor is valid LastActor ！= ThisActor 取消高亮LastActor 高亮ThisActor
	// 5. LastActor is valid   ThisActor is valid LastActor == ThisActor 不需要任何操作

	// 只需要在两个指针不同的时候，进行处理即可，然后判断当前变量指针是否存在，存在再调用对应的函数即可。
	if (ThisActor != LastActor)
	{
		if (ThisActor)
		{
			ThisActor->ShouldHighlightActor(true);
		}

		if (LastActor)
		{
			LastActor->ShouldHighlightActor(false);
		}
	}
}

void AAuraPlayerController::AutomaticPathfinding()
{
	if (!bAutomaticMovement)
	{
		return;
	}

	if (APawn* PossessedPawn = GetPawn())
	{
		// 距离Actor位置最近的样条线点
		const FVector LocationOnSpline = SplineComponent->FindLocationClosestToWorldLocation(
			PossessedPawn->GetActorLocation(),
			ESplineCoordinateSpace::World
		);
		// 距离目标点最近的样条线点的切线方向
		const FVector Direction = SplineComponent->FindDirectionClosestToWorldLocation(
			LocationOnSpline,
			ESplineCoordinateSpace::World
		);
		PossessedPawn->AddMovementInput(Direction);
		const float DistanceToDestination = (ClickPosition - LocationOnSpline).Length();
		if (DistanceToDestination <= AutomaticMovementRadius)
		{
			bAutomaticMovement = false;
		}
	}
}
